const path = require("path");
const NAME = /^[a-zA-Z0-9\u4e00-\u9fa5\:\-_]+$/;
function isName (name) {
	if(typeof name !== "string") {return false;}
	return NAME.test(name);
}
//默认创建者
const Xcreator = {
	async base ({task, async: parallel, strict}) {
		if (!(task instanceof Array)) {return null;}
		task = await Promise.all(task.map(t => this.task(t)));
		if (strict) {
			task.map(x => {if (!x) {task = null;}});
		} else {
			task = task.filter(x => x);
		}
		if (!task) {return null;}
		if (parallel) {
			return function (arg) {
				return Promise.all(task.map(x=>x(arg, this)));
			} 
		}
		return async function (arg) {
			for(let i = 0, l = task.length; i < l; i++) {
				arg = await task[i](arg, this);
			}
			return arg;
		}
	}
}

//创建者代理配置
const creatorProxy = {
	apply (creator, object, args) {return creator.create(...args);},
	construct (creator, args) {return creator.new(...args);}
}
//任务代理配置
const taskProxy = {
	apply (task, object, args) {return task.exec(...args);},
	construct (task, args) {return task.new(...args);}
}
//监视者代理配置
const watcherProxy = {
	apply (watcher, object, args) {return watcher.watch(...args);},
	construct (watcher, args) {return watcher.new(...args);}
}
//监视状态代理配置
const watchStateProxy = {
	apply (watchState, object, args) {return watchState.stop(...args);},
	construct (watchState, args) {return watchState.new(...args);}
}
/**
 * 信息输出(默认)
 * @param  {String} options.lv 等级
 * @param  {String} options.tp 类型
 * @param  {String} options.at 所在模块
 * @param  {String} options.fn 文件名
 * @param  {Number} options.ln 行号
 * @param  {Number} options.cl 列号
 * @param  {String} options.tx 错误信息
 */
function print({lv, tp, at, fn, ln, cl, tx}) {
	if (tp) {tp = ":"+ tp;}
	if (at) {at = "@"+ at;}
	if (path.isAbsolute(fn)) {fn = path.relative (this.XBWP, fn);}
	tx.replace(/\\/g,"\\\\").replace(/\n/g,"\\n").replace(/\r/g,"\\r").replace(/\f/g,"\\f")
	console.log(`${lv}${tp}${at}\t[${fn}:${ln}:${cl}]\t${tx}`);
}
/**
 * Xauto代理
 */
let XautoProxy = {
	async apply (Xauto, object, [{
		opt, pkg, echo, XBWP, XPBP, 
		plugin = [], 
		creator = {},
		task = {}, 
		watcher = {}, 
		exec = [], 
		watch = {}, 
		script, 
	} = {}]) {
		const xauto = new Xauto({opt, pkg, echo, XBWP, XPBP});

		if (!(plugin instanceof Array)) {plugin  = [];}
		if (typeof creator !== "object" || !creator) {creator = {};}
		if (typeof task !== "object" || !task) {task = {};}
		if (typeof watcher !== "object" || !watcher) {watcher = {};}
		if (!(exec instanceof Array)) {exec = [];}
		if (typeof watch !== "object" || !watch) {watch = {};}
		if (typeof script !== "string" || !script) {script = '';}

		for (let k in Xcreator) {await xauto.creator(Xcreator[k], `Xauto:${k}`);}

		for (let i = 0, l = plugin.length; i < l; i++) {await xauto.plugin(plugin[i]);}

		for (let k in creator) {await xauto.creator(creator[k], k);}
		for (let k in task) {await xauto.task(task[k], k);}
		for (let k in watcher) {await xauto.watcher(watcher[k], k);}
		exec.map(e => xauto.exec(e));
		for (let k in watch) {
			let ws = watch[k];
			if(!(ws instanceof Array)) {continue;}
			xauto.register(await xauto.watch(...ws), k);
		}
		if (script) {
			script = path.resolve(xauto.XBWP, script);
			try {script = require(script);} catch(e) {}
			if(typeof script === "function") {
				try {script(Xauto);} catch(e) {}
			}
		}
		return xauto;
	}
}
/**
 * 上下文this
 */
let Context = (_ => {
	let contextProxy = {
		apply (context, object, args) {
			context.echo(...args);
		},
		setPrototypeOf () {return false;},
		getPrototypeOf () {return Context.prototype;}
	};
	let proxy = {
		apply (Context, object, [c,x]) {
			if (c instanceof Context) {return c;}
			return proxy.construct(Context, [x]);
		},
		construct (Context, [xauto]) {
			let context = new Context();
			Object.setPrototypeOf(context, xauto);
			Object.defineProperties(context, {$: {value:{}, enumerable: true}, });
			return new Proxy(context, contextProxy);
		}
	}
	return new Proxy(class Context extends Function {}, proxy);
})();
/**
 * 任务
 */
class Task extends Function {
	constructor (task, opt, ...args) {
		super();
		if (typeof opt!="object") opt = {};
		if (task instanceof Task) {
			this._task = task._task;
			this.Xauto = task.Xauto;
			this._opt = Object.assign({}, task._opt, opt);
			let _args = this._args = Object.create(task._args);
			args.map(x => _args.push(x));
		} else if (task instanceof Array && isFunction(task[0]) && task[1] instanceof Xauto) {
			this._task = task[0];
			this.Xauto = task[1];
			this._opt = Object.assign({}, opt);
			let _args = this._args = [];
			args.map(x => _args.push(x));
		} else {
			throw new Error("非法的task");
		}
	}
	/**
	 * 执行此任务
	 * @param  {any} arg     任务的参数
	 * @param  {Context} context 上下文this
	 * @return {any}         任务返回值
	 */
	async exec (arg, context) {
		return await this._task.call(Context(context, this.Xauto), arg, this._opt, ...this._args);
	}
	/**
	 * 基于当前任务创建
	 * @param  {any}    opt  选项
	 * @param  {...any} args 参数
	 * @return {Task}        创建的任务
	 */
	new (...args) {
		return new Proxy(new Task(this, ...args), taskProxy);
	}
}

class WatchState extends Function {
	constructor () {
		super();
		this._watching = true;
	}
	get watching () {
		return this._watching;
	}
	get watch () {
		return this._watcher;		
	}
	emit (arg) {
		if (!this._watching) {return;}
		this._task(arg).catch(handle.bind(this.Xauto));
	}
	async stop () {
		if (!this._watching) { return ;}
		this._watching = false;
		try {this._handle.call(null);} catch (e){}
	}
	new (...args) {
		return this._watcher.new(...args);
	}
}
/**
 * 监视者
 */
class Watcher extends Function {
	constructor (watcher, opt, ...args) {
		super();
		if (typeof opt !== "object") {opt = {};};
		if (watcher instanceof Watcher) {
			this._watcher = watcher._watcher;
			this.Xauto = watcher.Xauto;
			this._opt = Object.assign({}, watcher._opt, opt);
			let _args = this._args = Object.create(watcher._args);
			args.map(x => _args.push(x));
		} else if (watcher instanceof Array && isFunction(watcher[0]) && watcher[1] instanceof Xauto) {
			this._watcher = watcher[0];
			this.Xauto = watcher[1];
			this._opt = Object.assign({}, opt);
			let _args = this._args = [];
			args.map(x => _args.push(x));
		} else {
			throw new Error("非法的watcher");
		}
	}
	/**
	 * 开始监听
	 * @param  {Task}		task	事件发生后执行的任务
	 * @return {WatchState}			产生的事件监听
	 */
	async watch (task) {
		task = await this.Xauto.task(task);
		if (!task) {return null;}
		if (task.Xauto !== this.Xauto) {throw new Error("监视者与任务属于同一个Xauto");}
		let ws = new WatchState();
		ws.Xauto = this.Xauto;
		ws._watcher = this;
		ws._task = task;
		try {
			ws._handle = await this._watcher.call(new Context(this.Xauto), ws.emit.bind(ws), this._opt, ...this._args);
			return new Proxy(ws, watchStateProxy);
		} catch (e) {
			handle.call(this.Xauto, e);
		}
	}
	/**
	 * 基于当前监视者创建
	 * @param  {any}		opt		选项
	 * @param  {...any}		args	参数
	 * @return {Watcher}			创建的监视者
	 */
	new (opt, ...args) {
		return new Proxy(new Watcher(this, opt, ...args), watcherProxy);
	}
}

class Creator extends Function {
	constructor (creator, opt, ...args) {
		super();
		if (typeof opt !== "object") {opt = {};};
		if (creator instanceof Creator) {
			this._creator = creator._creator;
			this.Xauto = creator.Xauto;
			this._opt = Object.assign({}, creator._opt, opt);
			let _args = this._args = Object.create(creator._args);
			args.map(x => _args.push(x));
		} else if (creator instanceof Array && isFunction(creator[0]) && creator[1] instanceof Xauto) {
			this._creator = creator[0];
			this.Xauto = creator[1];
			this._opt = Object.assign({}, opt);
			let _args = this._args = [];
			args.map(x => _args.push(x));
		} else {
			throw new Error("非法的creator");
		}
	}
	/**
	 * 创建任务
	 * @param  {Object}		cfg		创建任务的参数
	 * @return {Task}				创建的任务
	 */
	async create (cfg) {
		let task = await this._creator.call(Context(null, this.Xauto), cfg, this._opt, ...this._args);
		if(typeof task === "function") {task = new Proxy(new Task([task,this.Xauto]), taskProxy);}
		else if(!(task instanceof Task)) {task = null;}
		return task;
	}
	/**
	 * 基于当前创建者创建
	 * @param  {any}    opt		选项
	 * @param  {...any} args	参数
	 * @return {Creator}		创建的创建者
	 */
	new (opt, ...args) {
		return new Proxy(new Creator(this, opt, ...args), creatorProxy);
	}
}


class XautoError extends Error {}

class Xauto {
	/**
	 * 构造Xauto
	 * @param	{Object}	options.opt		插件配置
	 * @param	{Object}	options.pkg		程序包配置
	 * @param	{Function}	options.echo	信息输出函数
	 * @param	{String}	options.XBWP	Xauto基本工作路径
	 * @param	{String}	options.XPBP	Xauto插件基本路径
	 */
	constructor({
		opt = {},
		pkg = {}, 
		echo = print, 
		XBWP = process.cwd(), 
		XPBP = "", 
	}) {
		if (!opt || typeof opt !== "object") {opt = {};}
		if (!pkg || typeof pkg !== "object") {pkg = {};}
		if (typeof echo !== "function") {echo = print;}
		if (typeof XBWP !== "string") {XBWP = "";}
		if (typeof XPBP !== "string") {XPBP = "";}
		if (!path.isAbsolute(XBWP)) {XBWP = path.resolve(process.cwd(), XBWP);}
		if (!path.isAbsolute(XPBP)) {XBWP = path.resolve(XBWP, XPBP);}
		Object.defineProperties(this, {
			_task: {value:{}, enumerable: true}, 
			_creator: {value:{}, enumerable: true}, 
			_watcher: {value:{}, enumerable: true}, 
			_watchState: {value:{}, enumerable: true}, 
			_plugin: {value:new Set(), enumerable: true}, 

			_print: {value:echo, enumerable: true}, 

			opt: {value:Object.freeze(opt), enumerable: true}, 
			pkg: {value:Object.freeze(pkg), enumerable: true}, 
			XBWP: {value:XBWP, enumerable: true}, 
			XPBP: {value:XPBP, enumerable: true}, 

			util: {value:{}, enumerable: true}, 

			creator: {value:this.creator, enumerable: true}, 
			task: {value:this.task, enumerable: true}, 
			watcher: {value:this.watcher, enumerable: true}, 
			watchState: {value:this.watchState, enumerable: true}, 
			register: {value:this.register, enumerable: true}, 
			plugin: {value:this.plugin, enumerable: true}, 
			exec: {value:this.exec, enumerable: true}, 
			throw: {value:this.throw, enumerable: true}, 
			echo: {value:this.echo, enumerable: true}, 
		});
	}
	/**
	 * 创建或获取任务
	 * @param	{any}		task	用于创建或获取任务的配置
	 * @param	{String}	name	注册名
	 * @return	{Task}				任务
	 */
	async task (task, name) {
		let tasks = this._task;
		if (typeof task === "string") {
			//获取任务
		 	if (task in tasks) {task = tasks[task];}
		 	else {task = null;}
		} else if (task instanceof Task) {
			task = task;
		} else if (task instanceof Array) {
			let [t, opt, ...args] = task;
			task = t;
			if (typeof task === "string") {
			 	if (task in tasks) {task = tasks [task];} 
			 	else {task = null;}
			} else if (isFunction(task)) {
				task = [task, this];
			} else if (!(task instanceof Task)) {
				task = null;
			}
			if (task) {task = new Proxy(new Task(task, opt, ...args), taskProxy);}
		} else if (isFunction(task)) {
			task = [task, this];
			task = new Proxy(new Task(task), taskProxy);
		} else if (typeof task === "object") {
			let creator = await this.creator(task['']);
			if (!creator) {task = null;}
			if (task) {task = await creator(task);}
		} else {
			task = null;
		}
		if (task && isName(name) && !(name in tasks)) {tasks[name] = task;}
		return task;
	}
	/**
	 * 创建或获取监视者
	 * @param	{any}		watcher	用于创建或获取监视者的配置
	 * @param	{String}	name	注册名
	 * @return	{Watcher}			监视者
	 */
	async watcher (watcher, name) {
		const List = this._watcher;
		if (typeof watcher === "string") {
		 	if (watcher in List) {watcher = List[watcher];}
		 	else {watcher = null;}
		} else if (watcher instanceof Watcher) {
			watcher = watcher;
		} else if (isFunction(watcher)) {
			watcher = [watcher, this];
			watcher = new Proxy(new Watcher(watcher), watcherProxy);
		} else if (watcher instanceof Array) {
			let [w, opt, ...args] = watcher;
			watcher = w;
			if (typeof watcher === "string") {
			 	if (watcher in List) {watcher = List [watcher];} 
			 	else {watcher = null;}
			} else if (isFunction(watcher)) {
				watcher = [watcher, this];
			} else if (!(watcher instanceof Task || isFunction(watcher))) {
				watcher = null;
		 	}
		 	if (watcher) {watcher = new Proxy(new Watcher(watcher, opt, ...args), watcherProxy);}
		} else {
			watcher = null;
		}
		if (watcher && isName(name) && !(name in List)) {List[name] = watcher;}
		return watcher;
	}

	/**
	 * 获取监视状态
	 * @param	{String}	name	监视状态名
	 * @return	{Watcher}			监视状态
	 */
	async watchState (watchState) {
		const List = this._watchState;
		if (typeof watchState === "string") {
		 	if (watchState in List) {watchState = List[watchState];}
		 	else {watchState = null;}
		} else {
			watchState = null;
		}
		return watchState;
	}
	/**
	 * 创建或获取创建者
	 * @param	{any}		watcher	用于创建或获取创建者的配置
	 * @param	{String}	name	注册名
	 * @return	{Creator}			创建的创建者
	 */
	async creator (creator, name) {
		const List = this._creator;
		if (typeof creator === "string") {
		 	if (creator in List) {creator = List[creator];}
		 	else {creator = null;}
		} else if (creator instanceof Creator) {
			creator = creator;
		} else if (isFunction(creator)) {
			creator = new Proxy(new Creator([creator, this]), creatorProxy);
		} else if (creator instanceof Array) {
			let [c, opt, ...args] = creator;
			creator = c;
			if (typeof creator === "string") {
			 	if (creator in List) {creator = List [creator];} 
			 	else {creator = null;}
			} else if (isFunction(creator)) {
				creator = [creator, this];
			} else if (!(creator instanceof Task)) {
				creator = null;
		 	}
		 	if (creator) {creator = new Proxy(new Creator(creator, opt, ...args), creatorProxy);}
		} else {
			creator = null;
		}
		if (creator && isName(name) && !(name in List)) {List[name] = creator;}
		return creator;

	}
	/**
	 * 注册任务/监视者/监视状态
	 * @param	{Object}	obj		要注册的任务/监视者/监视状态
	 * @param	{String}	name	注册名
	 * @return	{Boolean}			是否注册成功
	 */
	async register (obj, name) {
		let list = null;
		if (obj instanceof Task) {list = this._task;} 
		else if (obj instanceof Watcher) {list = this._watcher;} 
		else if (obj instanceof WatchState) {list = this._watchState;} 
		else if (obj instanceof Creator) {list = this._creator;} 
		else {return false;}
		if (obj.Xauto !== this) {return false;}
		if (!isName(name)) {return false;}
		if (name in list) {return false;}
		list[name] = obj;
		return true;

	}
	/**
	 * 装载插件(插件只会装载一次)
	 * @param	{String}	name	插件名
	 * @return	{Boolean}			是否已经载入且载入成功
	 */
	async plugin (name) {
		let List = this._plugin;
		if (typeof name !== "string") {return false;}
		if (!name) {return false;}
		if (name === "Xauto") {return true;}
		if (List.has(name)) {return true;}
		let plugin = "xauto-plugin-" + name;
		try {
			plugin = require(plugin);
		}catch(e){
			let p = this.XPBP;
			for(;;){
				try {
					plugin = require(p + "/node_modules/" + plugin);
					break;
				} catch(e) {}
				try {
					plugin = require(p + "/" + plugin);
					break;
				} catch(e) {}
				let np = path.dirname(p);
				if (np === p) {return false;}
				p = np;
			}
		}
		if (typeof plugin === "function") {
			try{let plugin = await plugin(this);} 
			catch (e) {return false;}
		}
		if (typeof plugin !== "object" || !plugin) {return false;} 
		let task = plugin.task;
		let creator = plugin.creator;
		let watcher = plugin.watcher;
		if (task) {for (let k in task) {await this.task(task[k], `${name}:${k}`);}}
		if (creator) {for (let k in creator) {await this.creator(creator[k], `${name}:${k}`);}}
		if (watcher) {for (let k in watcher) {await this.watcher(watcher[k], `${name}:${k}`);}}
		List.add(name);
		return true;
	}
	/**
	 * 执行一个任务
	 * @param	{Task}		task	要执行的任务
	 * @return	{Object}			任务结束返回的值
	 */
	async exec (task, cfg, context) {
		task = this.task(task);
		if (!task) {return false;}
		try {await task(cfg, context);} 
		catch (e) {handle.call(this, e);}
		return true;
	}
	/**
	 * 开始监视
	 * @param	{Watcher}		watcher	监视者
	 * @param	{Task}			task	任务
	 * @return	{WatchState}			监视状态
	 */
	async watch (watcher, task) {
		watcher = await this.watcher(watcher);
		if (!watcher) {return null;}
		task = await this.task(task);
		if (!task) {return null;}
		return await watcher(task);
	}
	/**
	 * 输出信息
	 * @param  {String} options.lv 等级
	 * @param  {String} options.tp 类型
	 * @param  {String} options.at 所在模块
	 * @param  {String} options.fn 文件名
	 * @param  {Number} options.ln 行号
	 * @param  {Number} options.cl 列号
	 * @param  {String} options.tx 错误信息
	 */
	echo ({lv = "info", tp, at, fn, ln, cl, tx} = {}) {
		if (typeof lv === "string") {lv = lv.replace(/[^a-zA-Z0-9 \-_]+/g, "").replace(/ /g, "-");} else {lv = "";}
		if (typeof tp === "string") {tp = tp.replace(/[^a-zA-Z0-9 \-_]+/g, "").replace(/ /g, "-");} else {tp = "";}
		if (typeof at === "string") {at = at.replace(/[^a-zA-Z0-9 \-_\:]+/g, "").replace(/ /g, "-");} else {at = "";}
		if (typeof fn === "string") {fn = fn.replace(/[\r\n\t\f\:"'`]+/g, "");} else {fn = "";}
		if (typeof ln !== "number") {ln = "";}
		if (typeof cl !== "number") {cl = "";}
		if (typeof tx !== "string") {tx = "";}
		this._print.call(this, {lv, tp, at, fn, ln, cl, tx});
	}
	/**
	 * 抛出错误信息并结束当前任务
	 */
	throw (output) {
		this.echo(output);
		throw new XautoError();
	}
}

/**
 * 是否为函数
 * @param	{Object}	func	被判断的对象
 * @return	{Boolean}			是否为函数
 */
function isFunction(func) {
	if (func instanceof Xauto) {return false;} 
	else if (func instanceof Context) {return false;} 
	else if (func instanceof Task) {return false;} 
	else if (func instanceof Creator) {return false;} 
	else if (func instanceof Watcher) {return false;} 
	else if (func instanceof WatchState) {return false;} 
	else if (typeof func === "function") {return true;} 
	else {return false;}
}

/**
 * 错误处理器
 * @param  {Object} e 意外的错误
 */
function handle (e) {
	if (e instanceof XautoError) {return;} 
	else if (e instanceof Error) {this.echo({tx:e.stack});} 
	else {this.echo(e);}
}

module.exports = new Proxy(Xauto, XautoProxy);
